package boardData;

import iodevice.CommandPhalanx;

import java.io.BufferedWriter;
import java.io.IOException;

import ChessEngine.PieceSquareTableEvaluator;
import Pieces.PieceValues.*;


/**
 * Klasse die ein Brett darstellt
 * Das Brett wird als 10x12 Array dargestellt, mit Position 21 als a8 und Position 98 als h1.
 * 
 *      0: 1: 2: 3: 4: 5: 6: 7: 8: 9:
 *  00: xx|xx|xx|xx|xx|xx|xx|xx|xx|xx
 *  10: xx|xx|xx|xx|xx|xx|xx|xx|xx|xx
 *  20: xx|a8|b8|c8|d8|e8|f8|g8|h8|xx
 *  30: xx|a7|b7|c7|d7|e7|f7|g7|h7|xx
 *  40: xx|a6|b6|c6|d6|e6|f6|g6|h6|xx
 *  50: xx|a5|b5|c5|d5|e5|f5|g5|h5|xx
 *  60: xx|a4|b4|c4|d4|e4|f4|g4|h4|xx
 *  70: xx|a3|b3|c3|d3|e3|f3|g3|h3|xx
 *  80: xx|a2|b2|c2|d2|e2|f2|g2|h2|xx
 *  90: xx|a1|b1|c1|d1|e1|f1|g1|h1|xx
 * 100: xx|xx|xx|xx|xx|xx|xx|xx|xx|xx
 * 110: xx|xx|xx|xx|xx|xx|xx|xx|xx|xx
 * 
 * @author Fabian Hewer
 * @version V1.0.0.001 (28.9.'11)
 *
 */
public class Board implements Cloneable {
	//Variablen
//	private BoardElement[] board = new BoardElement[120];
	private byte[] byteBoard = new byte[120];
	private int enPassant;		//Feld auf dem EnPassant möglich ist.
	private int movesDone = 0 ;	//Züge die bisher durchgeführt wurden. Erhöht wen schwarz gezogen hat.
//	private String castling;	//"QKqk" Rochademöglichkeiten.
//	private String sideToMove;  //Farbe die am Zug ist.
	private String currentMove; //Zug, der die Stellung hervorgebracht hat.
	private History history = new History();
//	private long zobirisKey = 0;
	//Booleans
	private boolean isSideWhite = true;
	private boolean casWhiteKingPos = false; //Rochade
	private boolean casWhiteQueenPos = false;//   %
	private boolean casBlackKingPos = false; //   %   
	private boolean casBlackQueenPos = false;//   %   
	
	//Konstanten
//	public static final int ILLEGAL_FIELD = 999;
//	public static final int EMPTY_FIELD = 0;	 //Immer Null...
//	public static final int WHITE_KING = 3;		 //Weiß immer positiv
//	public static final int BLACK_KING = -3;	 //Schwarz immer weiß
//	public static final int WHITE_QUEEN = 6;
//	public static final int BLACK_QUEEN = -6;
//	public static final int WHITE_PAWN = 1;
//	public static final int BLACK_PAWN = -1;
//	public static final int WHITE_BISHOP = 4;
//	public static final int BLACK_BISHOP = -4;
//	public static final int WHITE_KNIGHT = 2;
//	public static final int BLACK_KNIGHT = -2;
//	public static final int WHITE_ROOK = 5;
//	public static final int BLACK_ROOK = -5;
	
	//--// Konstruktoren
	public Board(String[] positionString) {
		String fen = positionString[0].substring(0);
		String moves;
		if(positionString[1] == null) {
			moves = "";
		} else {
			moves = positionString[1];
		}
		// Initalisieren des Feldes aus dem Fenstring.
		for(int field = 0; field < 120; field++) {
			if(field <= 19 || field >= 100 || field%10 == 9 || field%10 == 0) {
				byteBoard[field] = Pieces.PieceValues.ILLEGAL_FIELD;
			} else {
				char pieceOnField = fen.charAt(0);
				switch(pieceOnField){
					case 'p': byteBoard[field] = Pieces.PieceValues.BLACK_PAWN; break;
					case 'r': byteBoard[field] = Pieces.PieceValues.BLACK_ROOK; break;
					case 'n': byteBoard[field] = Pieces.PieceValues.BLACK_KNIGHT; break;
					case 'b': byteBoard[field] = Pieces.PieceValues.BLACK_BISHOP; break;
					case 'k': byteBoard[field] = Pieces.PieceValues.BLACK_KING; break;
					case 'q': byteBoard[field] = Pieces.PieceValues.BLACK_QUEEN; break;
					case 'P': byteBoard[field] = Pieces.PieceValues.WHITE_PAWN; break;
					case 'R': byteBoard[field] = Pieces.PieceValues.WHITE_ROOK; break;
					case 'N': byteBoard[field] = Pieces.PieceValues.WHITE_KNIGHT; break;
					case 'B': byteBoard[field] = Pieces.PieceValues.WHITE_BISHOP; break;
					case 'K': byteBoard[field] = Pieces.PieceValues.WHITE_KING; break;
					case 'Q': byteBoard[field] = Pieces.PieceValues.WHITE_QUEEN; break;
					case '8': byteBoard[field] = Pieces.PieceValues.EMPTY_FIELD; field++;
					case '7': byteBoard[field] = Pieces.PieceValues.EMPTY_FIELD; field++;
					case '6': byteBoard[field] = Pieces.PieceValues.EMPTY_FIELD; field++;
					case '5': byteBoard[field] = Pieces.PieceValues.EMPTY_FIELD; field++;
					case '4': byteBoard[field] = Pieces.PieceValues.EMPTY_FIELD; field++;
					case '3': byteBoard[field] = Pieces.PieceValues.EMPTY_FIELD; field++;
					case '2': byteBoard[field] = Pieces.PieceValues.EMPTY_FIELD; field++;
					case '1': byteBoard[field] = Pieces.PieceValues.EMPTY_FIELD; break;
					case '/': field--; break;
				}
			fen = fen.substring(1).trim();
			}
		}
		int tokenEnd;
		String token;
		for(int tokenNr = 0; tokenNr <= 4; tokenNr++){
			tokenEnd = fen.trim().indexOf(" ");
			if(tokenEnd < 0)
				token = fen.substring(0).trim();
			else
				token = fen.substring(0,tokenEnd).trim();
			switch (tokenNr){
			case 0: if(token.equals("w"))  //Token besagt welche Seite in der momentanen Stellung am Zug ist.
						isSideWhite = true;
					else
						isSideWhite = false;
					break;
			case 2: if(token.equals("-") || token.equals("o")) { //Token besagt auf welchem Feld enPassant möglich ist.
						enPassant = 0;
					} else {
						enPassant = toField(token);
					} break;
			case 1: if(token.indexOf("K") >= 0) //Token besagt welche Rochaden möglich sind.
						casWhiteKingPos = true;	//Werte sind -1 wenn die entsprechende Rochade nicht möglich ist.
					if(token.indexOf("Q") >= 0)	//dies trifft immer zu, da nach dem Token für Rochade nurnoch integertokens kommen. 
						casWhiteQueenPos = true;
					if(token.indexOf("k") >= 0)
						casBlackKingPos = true;
					if(token.indexOf("q") >= 0)
						casBlackQueenPos =true;
					break;
			case 3: if(token.equals("o")) {	//Token besagt wieviele Halbzüge nach 50-Zügeregel gezogen wurden.
				} else {
					history.setHistoryCount(Integer.parseInt(token));
				} break;
			case 4: if(token.equals("o")){
				} else {
					movesDone = Integer.parseInt(token);
				} break;
			}
			if(tokenEnd != -1)
				fen = fen.substring(tokenEnd).trim();
		}
		history.toHistory(this.BtoFen());
		int moveEnd;
		String currentMove;
		if(moves == null){}
		while(moves != null && !moves.isEmpty()) {
			moveEnd = moves.indexOf(' ');
			if(moveEnd < 0)
				currentMove = moves.substring(0).trim();
			else
				currentMove = moves.substring(0,moveEnd).trim();
			if((currentMove.length() == 5)) {//Bauernumwandlung
				int start = toField(currentMove.substring(0,2)); //Zuganfangsposition
				int destination = toField(currentMove.substring(2,4)); //Zugendposition
				char piece = currentMove.charAt(4);
				move(start, destination, piece);
			} else {
				int start = toField(currentMove.substring(0,2)); //Zuganfangsposition
				int destination = toField(currentMove.substring(2,4)); //Zugendposition
				move(start, destination);
			}
			if(moveEnd >= 0)
				moves = moves.substring(moveEnd).trim();
			else
				moves = null;

		}
//	System.out.println(toString()); //TODO TEST!!
//		zobirisKey = MainEngine.zobiGen.generateZobirisKey(this);
	}	
	
	public Board(Board board) {
		for(int square = 0; square < 120; square++) {
			this.byteBoard[square] = board.getPiece(square);
		}
		this.enPassant = board.enPassant;
		this.currentMove = board.currentMove;
		this.history = new History(board.history);
		this.isSideWhite = board.isSideWhite;
		this.casWhiteKingPos = board.casWhiteKingPos;
		this.casWhiteQueenPos = board.casWhiteQueenPos; 
		this.casBlackKingPos = board.casBlackKingPos;   
		this.casBlackQueenPos = board.casBlackQueenPos;
//		this.zobirisKey = board.zobirisKey;
	}
	//--//
	
//	public static void main(String[] Args) { //TODO TEST!!
//		String[] position = new String[2];
//		position[0] = "rnbqk2r/Q1pppppp/8/8/8/8/PpPPPPPP/R1B1K2R w QKqk - 0 1";
//		position[1] = "";
//		String positionFEN = "rnbqk2r/Q1pppppp/8/8/8/8/PpPPPPPP/R1B1K2R w QKqk - 0 1";
//		position[0] = positionFEN;
//		position[1] = "a7a6 b8c6 a6a7";
//		Board board = new Board(position);
//		Board testBoard = board.moveClone(43, 22);
//		board.move(43, 22);
//		PieceSquareTableEvaluator eval = new PieceSquareTableEvaluator();
//		CommandPhalanx.infoString("" + eval.evaluate(testBoard));
//		System.out.println("Durchsuche history:");
//		board.getHistory().matches(board);
//		CommandPhalanx.infoString("Zug 1");
//		board.move(31, 51);
//		System.out.println(board.toString());
//		CommandPhalanx.infoString("Zug 1 revert");
//		board.move(51, 31);
//		System.out.println(board.toString());
//		CommandPhalanx.infoString("Umwandlung");
//		board.move(82, 92, 'q');
//		System.out.println(board.toString());
//		CommandPhalanx.infoString("Rochade weiß");
//		board.move(95, 97);
//		System.out.println(board.toString());
//		CommandPhalanx.infoString("Turm geschlagen");
//		board.move(31, 21);
//		System.out.println(board.toString());
//		CommandPhalanx.infoString("Rochade schwarz");
//		board.move(25, 27);
//		board.move(95,97);
//		board.move(35,55);
//		System.out.println(board.toString());
//		System.out.println("FENtest");
//		System.out.println(board.BtoFen());
//	}
	
	/**
	 * Methode um ein Feld des Array Algebraisch (a1 ode so) darzustellen.
	 * @param fieldNr Nummer des Arrayfeldes
	 * @return Algebraische Schreibweise des Feldes.
	 */
	private String toAlgebraic(int fieldNr) {
		if(fieldNr <= 19 || fieldNr >= 100 || fieldNr%10 == 9 || fieldNr%10 == 0)
			return "n/v";
		else {
			String algebraic = "n/v";
			String algebraicRow = "/v";
			switch(fieldNr/10) {
			case 2: algebraicRow = ("8"); break;
			case 3: algebraicRow = ("7"); break;
			case 4: algebraicRow = ("6"); break;
			case 5: algebraicRow = ("5"); break;
			case 6: algebraicRow = ("4"); break;
			case 7: algebraicRow = ("3"); break;
			case 8: algebraicRow = ("2"); break;
			case 9: algebraicRow = ("1"); break;
			default: return algebraic;
			}
			switch(fieldNr%10) {
			case 1: algebraic = ("a" + algebraicRow); break;
			case 2: algebraic = ("b" + algebraicRow); break;
			case 3: algebraic = ("c" + algebraicRow); break;
			case 4: algebraic = ("d" + algebraicRow); break;
			case 5: algebraic = ("e" + algebraicRow); break;
			case 6: algebraic = ("f" + algebraicRow); break;
			case 7: algebraic = ("g" + algebraicRow); break;
			case 8: algebraic = ("h" + algebraicRow); break;
			default: return algebraic;
			}
			return algebraic;
		}
	}
	
	/**
	 * Methode um ein Feld in Algebraischer notation in die Arraystruktur umzubauen.
	 * @param algebraic Feld in Algebrascher notation
	 * @return Feld als Nummer.
	 */
	public int toField(String algebraic) {
		if(algebraic.length() != 2)
			return 0;
		else {
			int fieldNr = 0;
			char row = algebraic.charAt(0);
			char collumn = algebraic.charAt(1);
			switch(row) {
			case 'a': fieldNr+=1; break;
			case 'b': fieldNr+=2; break;
			case 'c': fieldNr+=3; break;
			case 'd': fieldNr+=4; break;
			case 'e': fieldNr+=5; break;
			case 'f': fieldNr+=6; break;
			case 'g': fieldNr+=7; break;
			case 'h': fieldNr+=8; break;
			default: break;
			}
			switch(collumn) {
			case '1': fieldNr+=90; break;
			case '2': fieldNr+=80; break;
			case '3': fieldNr+=70; break;
			case '4': fieldNr+=60; break;
			case '5': fieldNr+=50; break;
			case '6': fieldNr+=40; break;
			case '7': fieldNr+=30; break;
			case '8': fieldNr+=20; break;
			default: break;
			}
			return fieldNr;
		}
	}
	
	/**
	 * Experimentelle Methode, die einen Zug auf dem Brett ausführt
	 * @param start Feld auf dem die Figur steht die ziehen soll
	 * @param destination Feld auf das die Figur ziehen soll
	 */
	public void move(int start, int destination) {
		move(start, destination, 'x');
	}
	

	/**
	 * Methode, die einen Zug auf dem Brett ausführt.
	 * ZobristKey des Brettes wird automatisch aktualisiert.
	 * @param start Feld auf dem die Figur steht die ziehen soll
	 * @param destination Feld auf das die Figur ziehen soll
	 * @param piece Figur in die ein Bauer gewandelt werden soll ind Notation (qnbr)
	 */
	public void move(int start, int destination, char piece) {
//		zobirisKey ^= MainEngine.zobiGen.getValueCas(getCasWhite(), true);  //Rochademöglichkeiten raus
//		zobirisKey ^= MainEngine.zobiGen.getValueCas(getCasBlack(), false); //Rochademöglichkeiten raus
		int movedPiece = getPiece(start);
		int capturedPiece = getPiece(destination);
		int newEnPassant = 0;
		boolean promoted = false;
		if(movedPiece == Pieces.PieceValues.BLACK_KING && start == 25) { //Schwarz: Zug mit König von Startfeld
			if(destination == 23) { //Rochade lang
				setPiece(21, Pieces.PieceValues.EMPTY_FIELD);
				setPiece(24, Pieces.PieceValues.BLACK_ROOK);
//				zobirisKey ^= MainEngine.zobiGen.getValuePiece(Pieces.PieceValues.BLACK_ROOK, 21);
//				zobirisKey ^= MainEngine.zobiGen.getValuePiece(Pieces.PieceValues.BLACK_ROOK, 24);
				//Turmteil der Rochade.
			} else if(destination == 27) { //Rochade kurz
				setPiece(28, Pieces.PieceValues.EMPTY_FIELD);
				setPiece(26, Pieces.PieceValues.BLACK_ROOK);
//				zobirisKey ^= MainEngine.zobiGen.getValuePiece(Pieces.PieceValues.BLACK_ROOK, 28);
//				zobirisKey ^= MainEngine.zobiGen.getValuePiece(Pieces.PieceValues.BLACK_ROOK, 26);
				//Turmteil der Rochade.
			}
			casBlackQueenPos = false;
			casBlackKingPos = false;
		} else if(movedPiece == Pieces.PieceValues.WHITE_KING  && start == 95) { //Weiß: Zug mit König von Startfeld
			if(destination == 93) { //Rochade lang
				setPiece(91, Pieces.PieceValues.EMPTY_FIELD);
				setPiece(94, Pieces.PieceValues.WHITE_ROOK);
//				zobirisKey ^= MainEngine.zobiGen.getValuePiece(Pieces.PieceValues.WHITE_ROOK, 91);
//				zobirisKey ^= MainEngine.zobiGen.getValuePiece(Pieces.PieceValues.WHITE_ROOK, 94);
				//Turmteil der Rochade.
			} else if(destination == 97) { //Rochade kurz
				setPiece(98, Pieces.PieceValues.EMPTY_FIELD);
				setPiece(96, Pieces.PieceValues.WHITE_ROOK);
//				zobirisKey ^= MainEngine.zobiGen.getValuePiece(Pieces.PieceValues.WHITE_ROOK, 98);
//				zobirisKey ^= MainEngine.zobiGen.getValuePiece(Pieces.PieceValues.WHITE_ROOK, 96);
				//Turmteil der Rochade.
			}
			casWhiteKingPos = false;
			casWhiteQueenPos = false;
		} else if(movedPiece == Pieces.PieceValues.BLACK_PAWN){//Schwarz: Zug mit Bauer
			if (start/10 == 3 && destination/10 == 5)   //2-Felder Bauernzug
				newEnPassant = destination-10;
			else if(destination == enPassant) {		//EnPassantschlag
				setPiece(destination-10, Pieces.PieceValues.EMPTY_FIELD);
//				zobirisKey ^= MainEngine.zobiGen.getValuePiece(Pieces.PieceValues.WHITE_PAWN, destination+10);
				//Ausgleich EnPassantschlag.
			} else if(destination/10 == 9){
				byte newPiece = givePiece(piece, false);        //Umwandlung
				promoted = true;
				setPiece(start, newPiece);
			}
			history.resetHistory();						//Historyreset 50-Zügeregel
		} else if(movedPiece == Pieces.PieceValues.WHITE_PAWN){//Weiß: Zug mit Bauer
			if (start/10 == 8 && destination/10 == 6)   //2-Felder Bauernzug
				newEnPassant = destination+10;
			else if(destination == enPassant) {		//EnPassantschlag
				setPiece(destination+10, Pieces.PieceValues.EMPTY_FIELD);
//				zobirisKey ^= MainEngine.zobiGen.getValuePiece(Pieces.PieceValues.BLACK_PAWN, destination+10);
				//Ausgleich EnPassantschlag.
			} else if(destination/10 == 2) {
				byte newPiece = givePiece(piece, true);        //Umwandlung
				promoted = true;
				setPiece(start, newPiece);
			}
			history.resetHistory();						//Historyreset 50-Zügeregel
		} else if(movedPiece == Pieces.PieceValues.BLACK_ROOK){//Schwarz: Zug mit Turm
			if(start == 21) //von Startfeld Queenside
				casBlackQueenPos = false;
			if(start == 28) //von Startfeld Kingside
				casBlackKingPos = false;
		} else if(movedPiece == Pieces.PieceValues.WHITE_ROOK){//Weiß: Zug mit Turm
			if(start == 91) //von Startfeld Queenside
				casWhiteQueenPos = false;
			if(start == 98) //von Startfeld Kingside
				casWhiteKingPos = false;
		}
		//Ab hier wird die Methode immer durchgeführt.
		if(capturedPiece == Pieces.PieceValues.BLACK_ROOK){//Schwarzer Turm geschlagen
			if(destination == 21) //auf Startfeld Queenside
				casBlackQueenPos = false;
			if(destination == 28) //auf Startfeld Kingside
				casBlackKingPos = false;		
		} else if(capturedPiece == Pieces.PieceValues.WHITE_ROOK){//Weißer Turm geschlagen
			if(destination == 91) //auf Startfeld Queenside
				casBlackQueenPos = false;
			if(destination == 98) //auf Startfeld Kingside
				casBlackKingPos = false;		
		}
		setPiece(destination, getPiece(start)); 	//Das ist der Zug
		setPiece(start, Pieces.PieceValues.EMPTY_FIELD);//  %
		if(enPassant != 0)
//			zobirisKey ^= MainEngine.zobiGen.getValueEnPassant(enPassant); //Altes EPFeld raus (wenn vorhanden)
		enPassant = newEnPassant;
		if(enPassant != 0)
//			zobirisKey ^= MainEngine.zobiGen.getValueEnPassant(enPassant); //Neues EPFeld rein (wenn vorhanden)
		if(!isSideWhite){
			movesDone++;
		}
		isSideWhite = !isSideWhite;
//		zobirisKey ^= MainEngine.zobiGen.getValueSideToMove(); //Seitenwechsel
		if(promoted)
			currentMove = (toAlgebraic(start) + toAlgebraic(destination) + piece);
		else
			currentMove = (toAlgebraic(start) + toAlgebraic(destination));
		if(capturedPiece != Pieces.PieceValues.EMPTY_FIELD)
			history.resetHistory();									//Historyreset 50-Zügeregel
		history.toHistory(this.BtoFen());
//		System.out.println(toString()); //TODO Test
//		zobirisKey ^= MainEngine.zobiGen.getValueCas(getCasWhite(), true);  //Rochademöglichkeiten rein
//		zobirisKey ^= MainEngine.zobiGen.getValueCas(getCasBlack(), false); //Rochademöglichkeiten rein
//		if(capturedPiece != Pieces.PieceValues.EMPTY_FIELD);
//			zobirisKey ^= MainEngine.zobiGen.getValuePiece(capturedPiece, destination); //geschlagene Figur raus.
//		zobirisKey ^= MainEngine.zobiGen.getValuePiece(movedPiece, start); //Figur vom Startfeld entfernen
//		zobirisKey ^= MainEngine.zobiGen.getValuePiece(getPiece(destination), destination); //Figur auf Zielfeld hinzufügen (movedPiece kann hier nicht benutzt werden wegen umwandlung...)
//		CommandPhalanx.infoString("OnTheFly ZobirisKey:    "  + zobirisKey);
//		CommandPhalanx.infoString("Berechneter ZobirisKey: " + MainEngine.zobiGen.generateZobirisKey(this));
	}
	
	/**
	 * Methode die zu einer Notationsfigur den Integerwert zurückliefert
	 * (Nur Dame, Springer, TUrm und Läufer)                            
	 * @param piece Figur in Notation
	 * @return Integerwert der Figur.
	 */
	private byte givePiece(char piece, boolean side) {
		switch (piece) {
		  case 'q': if(side)
			  		return Pieces.PieceValues.WHITE_QUEEN;
		  			else
		  			return Pieces.PieceValues.BLACK_QUEEN;
		  case 'r': if(side)
			  		return Pieces.PieceValues.WHITE_ROOK;
		  			else
		  			return Pieces.PieceValues.BLACK_ROOK;
		  case 'n': if(side)
		  			return Pieces.PieceValues.WHITE_KNIGHT;
	  				else
	  				return Pieces.PieceValues.BLACK_KNIGHT;
		  case 'b': if(side)
	  				return Pieces.PieceValues.WHITE_BISHOP;
					else
					return Pieces.PieceValues.BLACK_BISHOP;
		  case 'x': return Pieces.PieceValues.EMPTY_FIELD;
		}
		return Pieces.PieceValues.EMPTY_FIELD;
	}

	/**
	 * Methode die das Brett Kopiert und auf dem kopierten Brett einen Zug ausführt und
	 * dieses dann zurückgibt.
	 * @param start Feld auf dem die Figur steht die ziehen soll
	 * @param destination Feld auf das die Figur ziehen soll
	 * @param piece Figur in die der Bauer gewandelt werden soll in Notation (qnbr)
	 * @return das neue Brett
	 */
	public Board moveClone(int start, int destination, char piece) {
		Board clone = new Board(this);
		clone.move(start, destination, piece);
		return clone;
	}
	
	/**
	 * Methode die das Brett Kopiert und auf dem kopierten brett einen Zug ausführt und
	 * dieses dann zurückgibt.
	 * @param start Feld auf dem die Figur steht die ziehen soll
	 * @param destination Feld auf das die Figur ziehen soll
	 * @return das neue Brett
	 */
	public Board moveClone(int start, int destination) {
		Board clone = moveClone(start, destination, 'x');
		return clone;
	}
	
	protected Object clone(){
		Board clone = new Board(this);
		return clone;
	}
	
	/**
	 * Methode die eine Kopie des Brettes aufgibt.
	 * @return
	 */
	public Board getClone(){
		return (Board) clone();
	}
	
	/**
	 * Methode die angibt, ob ein Feld nicht Gültig iss, leer ist oder welche Figur draufsteht.
	 * @param feld, dass überprüft werden soll.
	 * @return nicht Gültig, leer oder die Figur.
	 */	
	public byte getPiece(int square) {
//		return board[square].getPiece();
		return byteBoard[square];
	}
	
	private void setPiece(int square, byte piece){
		byteBoard[square] = piece;
	}
	
	/**
	 * Methode die überprüft, ob eine Stellung auf einem Brett gleich der auf diesem Brett ist
	 * @param testBoard Brett das überprüft werden soll
	 * @return true wenn sowohl Stellung der Figuren, enPassant felder und Rochademöglichkeiten gleich sind.
	 */
	public boolean matches(Board testBoard) {
		for(int field = 0; field < 120; field++) {
			if(!(byteBoard[field] == testBoard.byteBoard[field])){
				return false;}
		}
		if(!casWhiteKingPos == testBoard.casWhiteKingPos){
			return false;}
		if(!casWhiteQueenPos == testBoard.casWhiteQueenPos){
			return false;}
		if(!casBlackKingPos == testBoard.casBlackKingPos){
			return false;}
		if(!casBlackQueenPos == testBoard.casBlackQueenPos){
			return false;}
		if(!(enPassant == testBoard.enPassant)){
			return false;}
		return true;
	}
	/**
	 * Methode die die Farbe eines Feldes zurückgibt
	 * @param field Nummer des Feldes
	 * @return true wenn Weiß, false wenn schwarz.
	 */
	public boolean giveColour(int square) {
		byte pieceByte = getPiece(square);
		if(pieceByte == Pieces.PieceValues.ILLEGAL_FIELD)
			return false;
		else if(pieceByte > 0)
			return true;
		return false;
	}
	
	/**
	 * Gibt die Länge des Arrays des Brettes zurück
	 * @return länge des Brettfeldes
	 */
	public int getArrayLength() {
		return byteBoard.length;
	}
	
	/**
	 * Methode die ein Standartbrett in Grundaufstellung zurückgibt(zu Testzwecken implementiert)
	 * @return das Brett.
	 */
	public static Board giveDefaultBoard() {
		String[] position = new String[2];
		position[0] = "rnbkqbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBKQBNR w - QKqk 0 0";
		position[1] = "";
		Board board = new Board(position);
		return board;
	}
	
//	/**
//	 * Methode die die Rochademöglichkeiten zurückgibt.
//	 * @return String dder Rochademöglichkeiten ("QKqk")
//	 */
//	public String getCastlingPosibilities() {
//		return castling;
//	}
	
	/**
	 * Methode die zurückgibt, ob die kleine Rochade möglich ist-
	 * @return true wenn true und fals wenn nich
	 */
	public boolean getCasKingside() {
		if (isSideWhite)
		return casWhiteKingPos;
		return casBlackKingPos;
	}
	
	/**
	 * Methode die zurückgibt, ob die große Rochade möglich ist-
	 * @return true wenn true und fals wenn nich
	 */
	public boolean getCasQueenside() {
		if (isSideWhite)
		return casWhiteQueenPos;
		return casBlackQueenPos;
	}
	
	/**
	 * Methode die angibt welche Rochaden Weiß durchführen kann
	 * @return 0: Keine
	 * 		   1: Klein
	 * 		   2: Groß
	 * 		   3: Beide
	 */
	public int getCasWhite() {
		int value = 0;
		if(casWhiteKingPos)
			value += 1;
		if(casWhiteQueenPos)
			value += 2;
		return value;
	}
	
	/**
	 * Methode die angibt welche Rochaden Schwarz durchführen kann
	 * @return 0: Keine
	 * 		   1: Klein
	 * 		   2: Groß
	 * 		   3: Beide
	 */
	public int getCasBlack() {
		int value = 0;
		if(casBlackKingPos)
			value += 1;
		if(casBlackQueenPos)
			value += 2;
		return value;
	}
	
	/**
	 * Methode die zurückgibt, welche Farbe in der momentanen Stellung am Zug ist.
	 * @return die Farbe als boolean (true bei Weiß)
	 */
	public boolean isSideWhite() {
		return isSideWhite;
	}
	
	/**
	 * Methode die zurückgibt, auf welchem Feld EnPassant möglich ist.
	 * @return das Feld in Feldwert
	 */
	public int getEnPassant() {
		return enPassant;
	}
	
	/**
	 * Methode um den letzten gezogenen Zug (der, der zu der aktuellen stellung geführt hat) in algebraischer Notation abzurufen.
	 * @return den Zug (z.B: e7e9)
	 */
	 public String getCurrMove() {
		 return currentMove;
	 }
	 
	 /**
	  * Methode die angibt, wiviele Züge auf dem Board durchgeführt wurden.
	  * @return
	  */
	 public int getMovesDone() {
		 return movesDone;
	 }
	 
	 /**
	  * Methode die den ZobristKey des Boards zurückgibt.
	  * @return
	  */
//	 public long getZobirisKey() {
//		 return zobirisKey;
//	 }
	 
	 /**
	  * Methode die die History ausgibt.
	  * @return
	  */
	 public History getHistory() {
		 return history;
	 }
	 
	 /**
	  * Methode die Ausgubt ob ein bestimmtes Feld gültig ist oder nicht.
	  * @param field das überprüft werden soll
	  * @return true wenn teil des tatsächlischen Schachbrettes.
	  */
	 public boolean fieldValid(int field) {
//		 return board[field].isValid();
		 if(byteBoard[field] == Pieces.PieceValues.ILLEGAL_FIELD)
			 return false;
		 return true;
	 }
	 
	 /**
	  * Methode die das Board in enen Fenstring umwandelt.
	  * @return Fen des Aktuellen boards.
	  */
	 public String BtoFen(){
		 StringBuffer Fen = new StringBuffer(40);
		 for(int i=2; i <= 9; i++) {
			 int freeSquares = 0;
			 for(int j=1; j <= 8; j++) {
				 int pieceOnField = getPiece(i*10+j);
				 if(pieceOnField == Pieces.PieceValues.EMPTY_FIELD){//Freie Felder werden im Fen addiert.
					 freeSquares++;
				 }else{
					 if(freeSquares > 0){
						 Fen.append(freeSquares);//Einfügen eventueller freier Felder
						 freeSquares = 0;
					 }
					 //PieceToFen Switch
					 switch(pieceOnField){
				 		case Pieces.PieceValues.BLACK_BISHOP: Fen.append('b'); break;
				 		case Pieces.PieceValues.BLACK_KING: Fen.append('k'); break;
				 		case Pieces.PieceValues.BLACK_KNIGHT: Fen.append('n'); break;
				 		case Pieces.PieceValues.BLACK_PAWN: Fen.append('p'); break;
				 		case Pieces.PieceValues.BLACK_QUEEN: Fen.append('q'); break;
				 		case Pieces.PieceValues.BLACK_ROOK: Fen.append('r'); break;
				 		case Pieces.PieceValues.WHITE_BISHOP: Fen.append('B'); break;
				 		case Pieces.PieceValues.WHITE_KING: Fen.append('K'); break;
				 		case Pieces.PieceValues.WHITE_KNIGHT: Fen.append('N'); break;
				 		case Pieces.PieceValues.WHITE_PAWN: Fen.append('P'); break;
				 		case Pieces.PieceValues.WHITE_QUEEN: Fen.append('Q'); break;
				 		case Pieces.PieceValues.WHITE_ROOK: Fen.append('R'); break;
					 }
				 }
			 }
			 if(freeSquares > 0)
				 Fen.append(freeSquares);//Einfügen eventueller freier Felder
			 if(i<9)
				 Fen.append('/'); //Eine Reihe zu ende
		 }
		 Fen.append(" ");
		 //Zugseite
		 if(isSideWhite)
			 Fen.append("w ");
		 else
			 Fen.append("b ");
		 //Rochade
		 boolean castlingPossible = false;
		 if(casWhiteQueenPos) {
			 castlingPossible = true;
			 Fen.append('Q');
		 }
		 if(casWhiteKingPos) {
			 castlingPossible = true;
			 Fen.append('K');
		 }
		 if(casBlackQueenPos) {
			 castlingPossible = true;
			 Fen.append('q');
		 }
		 if(casBlackKingPos) {
			 castlingPossible = true;
			 Fen.append('k');
		 }
		 if(!castlingPossible)
			 Fen.append('-');
		 Fen.append(' ');
		 //EnPassantFeld
		 if(enPassant == 0)
			 Fen.append('-');
		 else
			 Fen.append(toAlgebraic(enPassant));
		 Fen.append(' ');
		 return Fen.toString();
	 }
	
	//ToString
	public String toString() {
		StringBuffer returnString = new StringBuffer(200);
		for(int i = 0; i < 120 ; i++){		
			String pieceString = "n/v";
			switch(getPiece(i)) {
				case Pieces.PieceValues.EMPTY_FIELD:	pieceString = ("___"); break;
				case Pieces.PieceValues.BLACK_BISHOP: 	pieceString = (" b "); break;
				case Pieces.PieceValues.BLACK_KING:		pieceString = (" k "); break;
				case Pieces.PieceValues.BLACK_KNIGHT:	pieceString = (" n "); break;
				case Pieces.PieceValues.BLACK_PAWN:		pieceString = (" p "); break;
				case Pieces.PieceValues.BLACK_QUEEN:	pieceString = (" q "); break;
				case Pieces.PieceValues.BLACK_ROOK:		pieceString = (" r "); break;
				case Pieces.PieceValues.WHITE_BISHOP:	pieceString = (" B "); break;
				case Pieces.PieceValues.WHITE_KING:		pieceString = (" K "); break;
				case Pieces.PieceValues.WHITE_KNIGHT:	pieceString = (" N "); break;
				case Pieces.PieceValues.WHITE_PAWN:		pieceString = (" P "); break;
				case Pieces.PieceValues.WHITE_QUEEN:	pieceString = (" Q "); break;
				case Pieces.PieceValues.WHITE_ROOK:		pieceString = (" R "); break;
			}
			returnString.append(pieceString);
			if(i%10 == 9)
				returnString.append("\n");
			else
				returnString.append(",");
		}
		if(currentMove != null)
			returnString.append("\n Current Move: '").append(currentMove).append("'");
		returnString.append("\n Anzahl der Züge: " ).append(movesDone);
		returnString.append("\n EP Feld: '").append(enPassant).append("'");
		returnString.append("\n cas q: " ).append(casBlackQueenPos);
		returnString.append("\n cas k: " ).append(casBlackKingPos);
		returnString.append("\n cas Q: " ).append(casWhiteQueenPos);
		returnString.append("\n cas K: " ).append(casWhiteKingPos);
//		returnString.append("\n ZobristKey: ").append(zobirisKey);
		return (returnString.toString());
	}

	/**
	 * Methode die das Board zuruckgibt (ähnlich der ToString, allerdings mit einem übergebbaren String vor jeder Zeile.
	 * @param indent der String, der vor jeder Zeile angezeigt werden soll
	 * @throws IOException wenn was mit dem Writer nicht stimmt (da kann die Methode dann nix für).
	 */
	public void print(String indent, BufferedWriter out) throws IOException {
		StringBuffer returnString = new StringBuffer(indent);
		for (int i = 0; i < 120; i++) {		
			String pieceString = "n/v";
			switch(getPiece(i)) {
				case Pieces.PieceValues.EMPTY_FIELD:	pieceString = ("___"); break;
				case Pieces.PieceValues.BLACK_BISHOP: 	pieceString = (" b "); break;
				case Pieces.PieceValues.BLACK_KING:		pieceString = (" k "); break;
				case Pieces.PieceValues.BLACK_KNIGHT:	pieceString = (" n "); break;
				case Pieces.PieceValues.BLACK_PAWN:		pieceString = (" p "); break;
				case Pieces.PieceValues.BLACK_QUEEN:	pieceString = (" q "); break;
				case Pieces.PieceValues.BLACK_ROOK:		pieceString = (" r "); break;
				case Pieces.PieceValues.WHITE_BISHOP:	pieceString = (" B "); break;
				case Pieces.PieceValues.WHITE_KING:		pieceString = (" K "); break;
				case Pieces.PieceValues.WHITE_KNIGHT:	pieceString = (" N "); break;
				case Pieces.PieceValues.WHITE_PAWN:		pieceString = (" P "); break;
				case Pieces.PieceValues.WHITE_QUEEN:	pieceString = (" Q "); break;
				case Pieces.PieceValues.WHITE_ROOK:		pieceString = (" R "); break;
			}
			returnString.append(pieceString);
			if (i % 10 == 9)
				returnString.append("\n" + indent);
			else
				returnString.append(",");
		}
		if(currentMove != null){
			returnString.append("\n" + indent + " Current Move: '").append(currentMove).append("'\n");
			returnString.append("\n" + indent + " EP Feld: '").append(enPassant).append("'");
			returnString.append("\n" + indent + " cas q: " ).append(casBlackQueenPos);
			returnString.append("\n" + indent + " cas k: " ).append(casBlackKingPos);
			returnString.append("\n" + indent + " cas Q: " ).append(casWhiteQueenPos);
			returnString.append("\n" + indent + " cas K: " ).append(casWhiteKingPos);
			returnString.append("\n");
		}
		out.write(returnString.toString());
	}
}
